from typing import Optional
from application.util.StringUtil import *
from application.mapper.NoteMapper import NoteMapper
from application.mapper.ImageMapper import ImageMapper
from application.model.UserModel import UserModel
from application.mapper.UserMapper import UserMapper
from application.util.RedisUtil import RedisUtil
from application.mapper.FollowMapper import FollowMapper
from application.enumeration.StatusCodeEnum import StatusCodeEnum
from application.exception.BasicException import BasicException
from application.util.TokenUtil import generate_token, clear_token
from application.entity.UserEntity import UserAdd, UserOut, UserLogin, UserSearchOut, UserEdit
from application.util.UserUtil import encode_password, verify_captcha, generate_username, to_user_search_out

# 实例化redis客户端
redis_client: RedisUtil = RedisUtil()


async def get_all_data(page: int, page_size: int) -> list[Optional[UserSearchOut]]:
    """
    获取所有用户数据 TODO 后续改成用户搜索
    :return: 简易数据用户列表
    """
    user_out_list: list = []
    for user_model in await UserMapper.get_data_list(page=page, page_size=page_size):
        user_out_list.append(await to_user_search_out(user_model=user_model))
    return user_out_list


async def register(user_add: UserAdd) -> None:
    """
    注册用户
    :param user_add: 用户输入信息
    :return:
    """
    # 验证手机号格式
    if user_add.phone:
        if not is_valid_phone_number(phone_number=str(user_add.phone)):
            raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="手机号格式错误！")
        # 判断手机号是否已存在
        if await UserMapper.orm_model.exists(phone=user_add.phone):
            raise BasicException(status_code=StatusCodeEnum.ALREADY_EXIST_ERROR.value, message="手机号已被注册！")
    # 验证邮箱格式
    if user_add.email:
        if not is_valid_email(text=user_add.email):
            raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="邮箱格式错误！")
        # 判断邮箱是否已存在
        if await UserMapper.orm_model.exists(email=user_add.email):
            raise BasicException(status_code=StatusCodeEnum.ALREADY_EXIST_ERROR.value, message="邮箱已被注册！")
        # 判断验证码是否正确
        if not verify_captcha(key=user_add.email, captcha=user_add.captcha):
            raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value, message="验证码错误！")
    # 验证密码格式
    if not is_valid_password(text=user_add.password):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value,
                             message="密码最少6位，包含字母与数字，且不能包含特殊字符！")
    # 加密密码
    user_add.password = encode_password(password=user_add.password)
    # 转换为字典
    user_dict: dict = user_add.model_dump()
    # 生成用户名
    user_dict.update(username=await generate_username())
    # 存入数据
    if not await UserMapper.insert(data=user_dict):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="服务器繁忙，请稍后重试！")


async def login(user_login: UserLogin) -> str:
    """
    登录
    :param user_login: 用户输入信息
    :return: Token
    """
    user_model: Optional[UserModel] = None
    if user_login.login_type == "username":
        # 验证密码格式
        if not is_valid_password(text=user_login.password):
            raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value,
                                 message="密码最少6位，包含字母与数字，且不能包含特殊字符！")
        user_model = await UserMapper.get_data_by_username(username=user_login.username)
    elif user_login.login_type == "email":
        # 验证邮箱格式
        if not is_valid_email(text=user_login.username):
            raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="邮箱格式错误！")
        user_model = await UserMapper.get_data_by_email(email=user_login.username)
    elif user_login.login_type == "phone":
        # 验证手机号格式
        if not is_valid_phone_number(phone_number=user_login.username):
            raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="手机号格式错误！")
        user_model = await UserMapper.get_data_by_phone(phone=int(user_login.username))
    # 验证用户是否存在
    if user_model is None:
        raise BasicException(status_code=StatusCodeEnum.NOT_FOUND_ERROR.value, message="该用户未注册！")
    # 如果是邮箱/手机号登录，判断验证码是否正确
    if user_login.login_type in ["email", "phone"]:
        # 判断验证码是否正确
        if not verify_captcha(key=user_login.username, captcha=user_login.captcha):
            raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value, message="验证码错误！")
    # 密码验证
    user_login.password = encode_password(password=user_login.password)
    if user_model.password != user_login.password:
        raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value, message="密码错误！")
    # 判断用户是否被禁用
    if user_model.is_disabled:
        raise BasicException(status_code=StatusCodeEnum.AUTHORITY_ERROR.value, message="该用户已被封禁！")
    # 生成Token
    return generate_token(user_id=user_model.id)


def logout(user_id: int) -> None:
    """
    登出
    :param user_id: 用户ID
    :return:
    """
    clear_token(user_id=user_id)


async def get_user_by_id(user_id: int) -> UserOut:
    """
    根据ID获取用户信息
    :param user_id: 用户ID
    :return: 用户详细信息
    """
    user_model: Optional[UserModel] = await UserMapper.get_data_by_id(data_id=user_id)
    if user_model is None:
        raise BasicException(status_code=StatusCodeEnum.NOT_FOUND_ERROR.value, message="该用户不存在！")
    # 构建输出
    user_out: UserOut = UserOut(**user_model.to_dict())
    # 设置输出关注和粉丝列表
    user_out.fans_num = await FollowMapper.orm_model.filter(follower_id=user_model.id).count()  # 粉丝数
    user_out.follow_num = await FollowMapper.orm_model.filter(followee_id=user_model.id).count()  # 关注数
    # 设置输出帖子数量
    user_out.note_num = await NoteMapper.orm_model.filter(user_id=user_model.id).count()  # 帖子数量
    return user_out


async def edit_user_info(user_model: UserModel, user_edit: UserEdit) -> None:
    """
    修改用户基本信息
    :param user_model: 用户ORM模型
    :param user_edit: 用户编辑基本信息实体类
    :return:
    """
    # 验证用户名是否被其他用户占用
    if user_model.username != user_edit.username and await UserMapper.get_data_by_username(username=user_edit.username):
        raise BasicException(status_code=StatusCodeEnum.ALREADY_EXIST_ERROR.value, message="该用户名已被使用！")
    # 修改用户信息
    user_update_dict: dict = user_model.to_dict()
    for key, value in user_edit.model_dump().items():
        user_update_dict[key] = value
    if not await UserMapper.update(data=user_update_dict):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="更新用户信息失败！")


async def edit_avatar(user_model: UserModel, avatar: str) -> None:
    """
    更新用户头像
    :param user_model: 当前用户ORM模型
    :param avatar: 头像URL
    :return:
    """
    # 验证头像URL
    if not is_valid_url(text=avatar):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="不是一个正确的URL")
    # 当前图片URL加密为MD5
    image_md5: str = md5_encode(text=avatar)
    # 查询当前图片MD5是否存在，存在则直接赋值给用户头像，不存在则创建
    if not await ImageMapper.get_data_by_field(md5=image_md5):
        # 存入图片表
        if not await ImageMapper.insert(data={"md5": image_md5, "url": avatar}):
            raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="头像更新失败，请稍后再试！")
    # 更新用户表
    user_model.avatar_image_id = image_md5
    if not await UserMapper.update(data=user_model.to_dict()):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="头像更新失败，请稍后再试！")


async def edit_email(user_model: UserModel, email: str, captcha: str) -> None:
    """
    更新用户邮箱
    :param user_model: 用户ORM模型
    :param email: 邮箱
    :param captcha: 邮箱验证码
    :return:
    """
    if not is_valid_email(text=email):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="邮箱格式错误！")
    # 验证邮箱验证码
    if not verify_captcha(key=email, captcha=captcha):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="邮箱验证码错误！")
    # 修改用户邮箱
    user_model.email = email
    if not await UserMapper.update(data=user_model.to_dict()):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="邮箱修改失败，请稍后再试！")


async def edit_phone(user_model: UserModel, phone: int) -> None:
    """
    更新用户手机号
    :param user_model: 用户ORM模型
    :param phone: 手机号
    :return:
    """
    if not is_valid_phone_number(phone_number=str(phone)):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="手机号格式错误")
    # 修改用户手机号
    user_model.phone = phone
    if not await UserMapper.update(data=user_model.to_dict()):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="手机号修改失败，请稍后再试！")


async def edit_password(user_model: UserModel, old_password: str, new_password: str) -> None:
    """
    修改用户密码
    :param user_model: 用户ORM模型
    :param old_password: 老密码
    :param new_password: 新密码
    :return:
    """
    if not is_valid_password(text=new_password):
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value,
                             message="密码格式错误，最少6位，包含字母或者数字或者非特殊字符！")
    if encode_password(password=old_password) != user_model.password:
        raise BasicException(status_code=StatusCodeEnum.BAD_REQUEST_ERROR.value, message="原密码错误，请重新输入！")
    user_model.password = encode_password(password=new_password)
    if not await UserMapper.update(data=user_model.to_dict()):
        raise BasicException(status_code=StatusCodeEnum.ERROR.value, message="密码修改失败，请稍后再试！")


